#pragma once

#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctime>
#include "Task.hpp"
using namespace std;

const int dafault_process_num = 5;

typedef function<void(int)> func; 

class Channel
{
public:
    Channel() {}
    // 构造函数
    Channel(int wfd, string name, pid_t target)
        : _wfd(wfd), _name(name), _target(target)
    {}

    void DebugPrint()
    {
        cout << "写端文件描述符：" << _wfd << " " << "管道名字：" << _name << " " << "子进程pid: " << _target << endl;
    }
    // 析构函数
    ~Channel() {}
    int Fd() { return _wfd; };
    string Name() {return _name;};
    pid_t Target() {return _target;};
    void Close() {close(_wfd);};
    void Wait()
    {
        pid_t rid=waitpid(_target,nullptr,0);
        (void)rid;
    }

private:
    int _wfd;      // 写端的文件描述符
    string _name;  // 管道的名字
    pid_t _target; // 子进程的pid
};

class ProcessPool
{
public:
    ProcessPool(int num=dafault_process_num):_processnum(num)
    {
        srand(time(nullptr));
    }

    ~ProcessPool(){}

    // 初始化进程池
    bool InitProcessPool(func cd)
    {
        for (int i = 0; i < _processnum; i++)
        {
            // 创建管道
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
                return false;
            // 创建子进程
            pid_t id = fork();
            if (id < 0)
                return false;
            if (id == 0)
            {
                //要想关闭一个，回收一个，就要解决后面的子进程的写端指向前面子进程的问题
                //出现问题的本质就是子进程继承了父进程的文件描述符表
                //所以要想正常回收，每个子进程在关闭自己的写端的同时，也要关闭指向每个子进程的写端
                //而写端的文件描述符就保存在channels中，也会被子进程所继承
                cout<<getpid()<<"关闭了: ";
                for(auto& c : channels)
                {
                    cout<<c.Fd();
                    c.Close();
                }
                cout<<endl;
                // 子进程
                // 子进程关闭写端
                close(pipefd[1]);
                // 子进程要干的事
                // sleep(10);
                cd(pipefd[0]);

                exit(1);
            }
            // 父进程
            // 父进程关闭读端
            close(pipefd[0]);
            string name = "pipe-" + to_string(i);
            Channel chan(pipefd[1], name, id);
            channels.emplace_back(chan);
        }
        return true;
    }

    // 2.父进程要控制子进程，完成读取任务
    // 那么就要先指定某一个子进程
    void CtrlProcessChild()
    {
        int index = 0;
        while (true)
        {
            int who = index;
            index++;
            index %= channels.size();

            //让子进程选择一个任务去执行
            int x=rand()%tasks.size();

            write(channels[who].Fd(), &x, sizeof(x));
            sleep(1);
        }
    }

    void CtrlProcessChild(int count)
    {
        int index = 0;
        int cnt = 1;
        while (count--)
        {
            int who = index;
            index++;
            index %= channels.size();

            //让子进程选择一个任务去执行
            int x=rand()%tasks.size();

            write(channels[who].Fd(), &x, sizeof(x));
            sleep(1);
        }
    }

    void WaitProcessChild()
    {
        //这里是关闭一个，等待一个
        //但是会衍生出新的问题：无法正常关闭
        for(auto& c : channels)
        {
            c.Close();
            c.Wait();
        }

        //这里是同一关闭，统一等待
        //1.让所有的子进程停下来
        for(auto c : channels)
        {
            c.Close();
        }
        //2.回收所有子进程的僵尸信息
        for(auto& c : channels)
        {
            c.Wait();
            cout<<"回收子进程："<< c.Target() <<endl;
        }
    }
private:
    vector<Channel> channels;
    int _processnum;//记录有多少个子进程
};
